package top.liebes;


import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;

public class Score {

    public Map<String, List<Double>> scoreOfCourse = new HashMap<>();
    public Map<String, List<ScoreEntity>> scoreOfStudent = new HashMap<>();
    public List<List<ScoreEntity>> hangList = new ArrayList<>();

    public void calculate(Map<Integer, Map<Integer, Object>> map) {
        List<ScoreEntity> list = new ArrayList<>();

        // 预处理
        for (int i = 1; i <= map.size(); i++) {
            try {
                String sno = String.valueOf(map.get(i).get(2));
                String courseNumber = String.valueOf(map.get(i).get(8));
                double score = Double.parseDouble(String.valueOf(map.get(i).get(12)));
                double credit = Double.parseDouble(String.valueOf(map.get(i).get(11)));
                String stats = String.valueOf(map.get(i).get(20));
                list.add(new ScoreEntity(sno, courseNumber, credit, score, stats));
            } catch (Exception e) {
                System.out.println(map.get(i).get(2));
                System.out.println(map.get(i).get(12));
                System.out.println(map.get(i).get(11));
                throw e;
            }

        }

        // 排序
        Collections.sort(list, (o1, o2) -> {
            String sno1 = o1.studentNumber;
            String sno2 = o2.studentNumber;

            String courseNumber1 = o1.courseNumber;
            String courseNumber2 = o2.courseNumber;

            double score1 = o1.score;
            double score2 = o2.score;

            if (sno1.equals(sno2)) {
                if (courseNumber1.equals(courseNumber2)) {
                    return Double.compare(score1, score2);
                } else {
                    return courseNumber1.compareTo(courseNumber2);
                }
            } else {
                return sno1.compareTo(sno2);
            }
        });

        // 计算挂科情况，如过修回来了，则统一按照60计算标准差
        String pSno = list.get(0).studentNumber;
        int pIndex = 0;
        for (int i = 0; i < list.size(); i++) {
            String sno = list.get(i).studentNumber;
            if (!sno.equals(pSno)) {
                parse(new ArrayList<>(list.subList(pIndex, i)));
                pSno = sno;
                pIndex = i;
            }
        }
        parse(list.subList(pIndex, list.size()));


        // 计算课程标准差
        Map<String, Pair<Double, Double>> avgOfCourse = new HashMap<>();
        for (Map.Entry<String, List<Double>> entry : scoreOfCourse.entrySet()) {
            String courseNumber = entry.getKey();
            List<Double> scoreList = entry.getValue();
            double sum = 0;
            int total = scoreList.size();
            for (int i = 0; i < total; i++) {
                sum += scoreList.get(i);
            }
            double avg = sum / total;
            sum = 0;
            for (int i = 0; i < total; i++) {
                sum += Math.pow(scoreList.get(i) - avg, 2);
            }
            sum /= total;
            sum = Math.sqrt(sum);
            sum += 0.0001;
            avgOfCourse.put(courseNumber, new Pair<>(avg, sum));
        }

        Map<String, Double> avgOfStudent = new HashMap<>();
        for (Map.Entry<String, List<ScoreEntity>> entry : scoreOfStudent.entrySet()) {
            double totalOfCredit = 0.0;
            String sno = entry.getKey();
            int totalOfCourse = entry.getValue().size();
            double sum = 0;
            double sigma0 = 0;
            double sigma1 = 0;
            double sigma = 0;
            double u = 0;

            List<ScoreEntity> scoreList = entry.getValue();
            for (int i = 0; i < scoreList.size(); i++) {
                ScoreEntity item = scoreList.get(i);
                totalOfCredit += item.credit;
                // 该课程平均分
                double meanScoreOfCourse = avgOfCourse.get(item.courseNumber).getKey();
                // 该课程标准差
                double avgScoreOfCourse = avgOfCourse.get(item.courseNumber).getValue();
                sum += item.credit * (item.score - meanScoreOfCourse) / avgScoreOfCourse;
                sigma0 += item.credit * avgScoreOfCourse * avgScoreOfCourse;
                sigma1 += item.credit;
                u += meanScoreOfCourse;
            }
            // 标准得分
            sum /= totalOfCredit;
            sigma = Math.sqrt(sigma0 / sigma1);

            double res = sum * sigma + u / totalOfCourse;

            avgOfStudent.put(sno, res);
        }

        try {
            File studentFile = new File("/Users/liebes/project/java/score/data/学生平均成绩.csv");
            File hangStudentFile = new File("/Users/liebes/project/java/score/data/学生缓考或挂科情况.csv");
            File avgOfCourseFile = new File("/Users/liebes/project/java/score/data/各门课程标准差.csv");

            studentFile.delete();
            hangStudentFile.delete();
            avgOfCourseFile.delete();

            studentFile.createNewFile();
            hangStudentFile.createNewFile();
            avgOfCourseFile.createNewFile();

            FileWriter studentFileWriter = new FileWriter(studentFile);
            BufferedWriter writer = new BufferedWriter(studentFileWriter);
            writer.write("学号,加权分数\n");
            for (Map.Entry<String, Double> entry : avgOfStudent.entrySet()) {
                writer.write(entry.getKey() + "," + entry.getValue() + "\n");
            }
            writer.flush();
            writer.close();
            studentFileWriter.close();

            writer = new BufferedWriter(new FileWriter(hangStudentFile));
            writer.write("学号,课程编号,学分,分数,状态\n");
            for (int i = 0; i < hangList.size(); i++) {
                for (int j = 0; j < hangList.get(i).size(); j++) {
                    writer.write(
                            hangList.get(i).get(j).studentNumber + "," +
                                    hangList.get(i).get(j).courseNumber + "," +
                                    hangList.get(i).get(j).credit + "," +
                                    hangList.get(i).get(j).score + "," +
                                    hangList.get(i).get(j).stats + "\n"
                    );
                }
            }
            writer.flush();
            writer.close();

            writer = new BufferedWriter(new FileWriter(avgOfCourseFile));
            writer.write("课程编号,平均分,标准差\n");
            for (Map.Entry<String, Pair<Double, Double>> entry : avgOfCourse.entrySet()) {
                writer.write(
                        entry.getKey() + "," +
                                entry.getValue().getKey() + "," +
                                entry.getValue().getValue() + "\n"
                );
            }
            writer.flush();
            writer.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public void parse(List<ScoreEntity> sublist) {
        if (!check(sublist)) {
            System.err.println("error");
        }
        // 用来记录缺考或者挂科记录
        List<ScoreEntity> hang = new ArrayList<>();
        // 去除重复课程记录
        // 1. 除缓考外，其他非正常状态均视为挂科，按0分处理
        // 2. 如有挂科，重修回来的分数按照60计算
        // 3. 如存在多个重复记录，切无上述状态，则保留最高的分数
        for (int i = 0; i < sublist.size() - 1; i++) {
            if (sublist.get(i).courseNumber.equals(sublist.get(i + 1).courseNumber)) {

                int j;
                // 记录这门课是否通过，只要存在一个记录没有通过（缓考除外），则标记为未通过，再次通过则按照60计算
                boolean isFailed = sublist.get(i).isFailed() || sublist.get(i + 1).isFailed();

                for (j = i + 2; j < sublist.size(); j++) {
                    if (!sublist.get(j).courseNumber.equals(sublist.get(i).courseNumber)) {
                        break;
                    }
                    isFailed = isFailed || sublist.get(j).isFailed();
                }
                for (int k = i; k < j - 1; j--) {
                    sublist.remove(k);
                }
                if (sublist.get(i).score > 59.5 && isFailed) {
                    sublist.get(i).score = 60;
                }
            }
        }


        for (int i = 0; i < sublist.size(); i++) {
            String sno = sublist.get(i).studentNumber;
            String courseNumber = sublist.get(i).courseNumber;
            double score = sublist.get(i).score;
            // 记录学生成绩
            if (score < 60) {
                hang.add(sublist.get(i));
            } else {
                // 记录课程信息，用来计算标准差
                scoreOfCourse.putIfAbsent(courseNumber, new ArrayList<>());
                scoreOfCourse.get(courseNumber).add(score);
                scoreOfStudent.putIfAbsent(sno, new ArrayList<>());
                scoreOfStudent.get(sno).add(sublist.get(i));
            }
        }
        if (hang.size() > 0) {
            hangList.add(hang);
        }
    }

    public boolean check(List<ScoreEntity> sublist) {
        if (sublist.size() == 1) {
            return true;
        }
        for (int i = 1; i < sublist.size(); i++) {
            String s1 = sublist.get(i).studentNumber;
            String s2 = sublist.get(i - 1).studentNumber;
            if (!s1.equals(s2)) {
                return false;
            }
        }
        return true;
    }
}

class ScoreEntity {
    public String studentNumber;
    public String courseNumber;
    public double credit;
    public double score;
    public String stats;

    public ScoreEntity(String studentNumber, String courseNumber, double credit, double score, String stats) {
        this.studentNumber = studentNumber;
        this.courseNumber = courseNumber;
        this.credit = credit;
        this.score = score;
        this.stats = stats;
    }

    public boolean isPass() {
        if ("正常".equals(stats) && score >= 59.6) {
            return true;
        }
        return false;
    }

    public boolean isDelay() {
        return "缓考".equals(stats);
    }

    public boolean isFailed() {
        if (isDelay()) {
            return false;
        }
        return !isPass();
    }

    @Override
    public String toString() {
        return "ScoreEntity{" +
                "studentNumber='" + studentNumber + '\'' +
                ", courseNumber='" + courseNumber + '\'' +
                ", score=" + score +
                '}';
    }
}
